﻿using System.Collections.Generic;
using Microsoft.TeamFoundation;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

namespace TfsRollUp.Core
{
    /// <summary>
    /// Alm Rollup fields, workitemtypes, teamprojects, and tfscollections
    /// </summary>
    public sealed class WorkItemFieldCollection
    {
        /// <summary>
        /// [TeamProject][WorkItemType][Field]
        /// </summary>
        private Dictionary<string, Dictionary<string, List<string>>> _fieldCollection;

        /// <summary>
        /// A workitem store
        /// </summary>
        private WorkItemStore _store;

        /// <summary>
        /// ctor
        /// </summary>
        private WorkItemFieldCollection()
        {
        }

        /// <summary>
        /// Add rollup fields from this store
        /// </summary>
        /// <param name="store"></param>
        private void AddStore(WorkItemStore store)
        {
            TeamFoundationTrace.MethodEntry();
            if (store == null)
            {
                return;
            }
            _store = store;
            _fieldCollection = new Dictionary<string, Dictionary<string, List<string>>>();
            AddProjects();
            TeamFoundationTrace.MethodExit();
        }

        /// <summary>
        /// Add the projects for this collection
        /// </summary>
        private void AddProjects()
        {
            TeamFoundationTrace.MethodEntry();
            foreach (Project teamProject in _store.Projects)
            {
                var projectCollection = new Dictionary<string, List<string>>();
                _fieldCollection.Add(teamProject.Name, projectCollection);
                AddWorkItemTypes(teamProject, projectCollection);
            }
            TeamFoundationTrace.MethodExit();
        }

        /// <summary>
        /// Add the workitem types for this project and collection
        /// </summary>
        /// <param name="teamProject"></param>
        /// <param name="projectCollection"></param>
        private void AddWorkItemTypes(Project teamProject, Dictionary<string, List<string>> projectCollection)
        {
            TeamFoundationTrace.MethodEntry();
            foreach (WorkItemType workItemType in teamProject.WorkItemTypes)
            {
                var fieldList = new List<string>();
                projectCollection.Add(workItemType.Name, fieldList);
                AddFields(workItemType, fieldList);
            }
            TeamFoundationTrace.MethodExit();
        }

        /// <summary>
        /// Add the field for this type, project, and collection
        /// </summary>
        /// <param name="workItemType"></param>
        /// <param name="fieldList"></param>
        private void AddFields(WorkItemType workItemType, List<string> fieldList)
        {
            TeamFoundationTrace.MethodEntry();
            foreach (FieldDefinition fieldDefinition in workItemType.FieldDefinitions)
            {
                if (IsTfsRollUpField(fieldDefinition) && !fieldList.Contains(fieldDefinition.ReferenceName))
                {
                    fieldList.Add(fieldDefinition.ReferenceName);
                    TeamFoundationTrace.Verbose("{0}.AddFields: Field {1} added as TfsRollUpField", Constants.TfsRollUpPlugin, fieldDefinition.ReferenceName);
                    var underlyingFieldName = fieldDefinition.ReferenceName.Substring(Constants.FieldNameTfsRollUpPrefix.Length);
                    fieldList.Add(underlyingFieldName);
                    TeamFoundationTrace.Verbose("{0}.AddFields: Field {1} added as TfsRollUpField", Constants.TfsRollUpPlugin, underlyingFieldName);
                }
            }
            TeamFoundationTrace.MethodExit();
        }

        /// <summary>
        /// Is this field an alm rollup field
        /// </summary>
        /// <param name="fieldDefinition"></param>
        /// <returns>True if field is alm rollup field</returns>
        private bool IsTfsRollUpField(FieldDefinition fieldDefinition)
        {
            TeamFoundationTrace.MethodEntry(fieldDefinition.ReferenceName);
            if (fieldDefinition == null)
            {
                TeamFoundationTrace.MethodExit();
                return false;
            }
            TeamFoundationTrace.MethodExit();
            return fieldDefinition.FieldType == FieldType.Double &&
                    fieldDefinition.ReferenceName.StartsWith(Constants.FieldNameTfsRollUpPrefix) &&
                    fieldDefinition.HelpText.Contains(Constants.EnabledText);
        }

        /// <summary>
        /// Do you have this this TeamProject, and this WorkItemType?
        /// </summary>
        /// <param name="teamProjectName"></param>
        /// <param name="workItemTypeName"></param>
        /// <returns></returns>
        private bool HasWorkItemType(string teamProjectName, string workItemTypeName)
        {
            return _fieldCollection.ContainsKey(teamProjectName) &&
                _fieldCollection[teamProjectName].ContainsKey(workItemTypeName);
        }

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="store"></param>
        public WorkItemFieldCollection(WorkItemStore store)
        {
            _store = store;
            _fieldCollection = new Dictionary<string, Dictionary<string, List<string>>>();
            AddStore(store);
        }

        /// <summary>
        /// Get the fields for this combination of project and workitem type
        /// </summary>
        /// <param name="teamProjectName"></param>
        /// <param name="workItemTypeName"></param>
        /// <returns>The Alm rollup fields found</returns>
        public string[] GetRollUpFieldList(string teamProjectName, string workItemTypeName)
        {
            TeamFoundationTrace.MethodEntry();
            if (HasWorkItemType(teamProjectName, workItemTypeName))
            {
                TeamFoundationTrace.MethodExit();
                return _fieldCollection[teamProjectName][workItemTypeName].ToArray();
            }
            TeamFoundationTrace.MethodExit();
            return null;
        }
    }
}